﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Common
{
    public class HalfLifeCache<Key, Value> : IDictionary<Key, Value>
    {
        private IDictionary<Key, Value> _current = new Dictionary<Key, Value>();
        private IDictionary<Key, Value> _old = new Dictionary<Key, Value>();

        private object synclock = new object();

        private Timer _halfLifeTimer;

        public static readonly TimeSpan DEFAULT_HALFLIFE = TimeSpan.FromMinutes(30);
        public HalfLifeCache()
            : this(DEFAULT_HALFLIFE)
        { }
        public HalfLifeCache(TimeSpan halfLife)
        {
            _halfLifeTimer = new Timer(this.HalfLifeCycle);
            HalfLife = halfLife;
        }

        private void HalfLifeCycle(Object ignored)
        {
            lock (synclock)
            {
                _old.Clear();
                IDictionary<Key, Value> temp = _old;                                
                _old = _current;
                _current = temp;
            }
        }

        private TimeSpan _halfLife;
        public TimeSpan HalfLife
        {
            get { return _halfLife; }
            set
            {
                _halfLifeTimer.Change(TimeSpan.Zero, value);
                _halfLife = value;
            }
        }

        public void Add(Key key, Value value)
        {
            lock (synclock)
            {
                _current.Add(key, value);
                _old.Remove(key);
            }
        }

        public bool ContainsKey(Key key)
        {
            lock (synclock)
            { return _current.ContainsKey(key) || _old.ContainsKey(key); }
        }

        public ICollection<Key> Keys
        {
            get
            {
                lock (synclock)
                { return _current.Keys.Concat(_old.Keys).ToList(); }
            }
        }

        public bool Remove(Key key)
        {
            lock (synclock)
            {
                bool success;
                success = _current.Remove(key);
                success |= _old.Remove(key);
                return success;
            }
        }

        public bool TryGetValue(Key key, out Value value)
        {
            lock (synclock)
            {
                if (_current.TryGetValue(key, out value))
                { return true; }
                else
                { return _old.TryGetValue(key, out value); }
            }
        }

        public ICollection<Value> Values
        {
            get
            {
                lock (synclock)
                { return _current.Values.Concat(_old.Values).ToList(); }
            }
        }

        public Value this[Key key]
        {
            get
            {
                lock (synclock)
                {
                    if (_current.ContainsKey(key))
                    { return _current[key]; }
                    else
                    { return _old[key]; }
                }
            }
            set
            {
                lock (synclock)
                {
                    _current[key] = value;
                    _old.Remove(key);
                }
            }
        }

        public void Add(KeyValuePair<Key, Value> item)
        {
            lock (synclock)
            {
                _current.Add(item);
                _old.Remove(item.Key);
            }
        }

        public void Clear()
        {
            lock (synclock)
            {
                _current.Clear();
                _old.Clear();
            }
        }

        public bool Contains(KeyValuePair<Key, Value> item)
        {
            lock (synclock)
            { return _current.Contains(item) || _old.Contains(item); }
        }

        public void CopyTo(KeyValuePair<Key, Value>[] array, int arrayIndex)
        {
            lock (synclock)
            {
                _current.ToList().Concat(_old.ToList()).ToList().CopyTo(array, arrayIndex);
            }
        }

        public int Count
        {
            get
            {
                lock (synclock)
                { return _current.Count + _old.Count; }
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(KeyValuePair<Key, Value> item)
        {
            lock (synclock)
            { return _current.Remove(item) || _old.Remove(item); }
        }

        public IEnumerator<KeyValuePair<Key, Value>> GetEnumerator()
        {
            return this.ToList().GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.ToList().GetEnumerator();
        }
    }
}
